#include <SDL.h>

#include "view.h"
#include "sprite.h"
#include "ship.h"
#include "map.h"
#include "control.h"

View g_view;
extern Sprite* g_cell_sprite;
extern Sprite* g_select_sprite;
extern Sprite* g_canmove_sprite;
extern Sprite* g_sprites;
extern Ship* g_ships;
extern Ship* g_select_ship;
extern Control g_control;
extern int g_num_ships;

int InMouseSelect(int x, int y) {
    return x == g_view.mouse_select.x && y == g_view.mouse_select.y;
}

int CanMove(int x, int y) {
    if (!g_select_ship)
        return 0;
    return CanMoveShip(g_select_ship, x, y);
}

void RecalcCellOffset() {
    g_view.first_cell.x = g_view.offset.x / g_view.cell.width;
    g_view.first_cell.y = g_view.offset.y / g_view.cell.height;

    g_view.first_cell_offset.x = - g_view.offset.x % g_view.cell.width;
    g_view.first_cell_offset.y = - g_view.offset.y % g_view.cell.height;

    /* test dump
    printf("offset (%d, %d) => map_pos [%d, %d] + offset [ %d, %d]\n",
            g_view.offset.x,
            g_view.offset.y,
            g_view.first_cell.x,
            g_view.first_cell.y,
            g_view.first_cell_offset.x,
            g_view.first_cell_offset.y);
    */
}

void InitView() {
    g_view.window.width = 640;
    g_view.window.height = 480;

    g_view.cell.width = 76;
    g_view.cell.height = 38;

    g_view.view.width = g_view.window.width / g_view.cell.width +
         (g_view.window.width % g_view.cell.width ? 1 : 0);
    g_view.view.height = g_view.window.height / g_view.cell.height +
         (g_view.window.height % g_view.cell.height ? 1 : 0) + 1;

    g_view.offset.x = 0;
    g_view.offset.y = 0;

    RecalcCellOffset();

    g_view.mouse_select.x =  -1;
    g_view.mouse_select.y =  -1;

    g_view.cell_buffer = 0;
}

void InitCell(SDL_Surface* surface) {
    g_view.cell_buffer = calloc(g_view.cell.width * g_view.cell.height, sizeof(char));
    SDL_LockSurface(surface);

    int x = 60;
    int y = 0;
    char* pixels = (char*)surface->pixels;

    int i, j;
    for (j = 0; j < g_view.cell.height; ++j)
        for (i = 0; i < g_view.cell.width; ++i)
            *(g_view.cell_buffer + j * g_view.cell.width + i) =
                *(pixels + surface->w * (y + j) + x + i);

    SDL_UnlockSurface(surface);
}

int GetCellColor(int x, int y) {
    return *(g_view.cell_buffer + y * g_view.cell.width + x);
}

/* direction of coords:
 *     ^ Y
 *    /
 *   /
 *  /
 *  \
 *   \
 *    \
 *     V  X
 *
 * cell coords -> map coords:
 *
 *  ( 0, 0)  ( 1, 1)
 *  ( 1, -1) ( 2, 0)
 */

void GetMapPos(int* x, int* y, int mouse_x, int mouse_y) {
    mouse_x -= g_view.first_cell_offset.x;
    mouse_y -= g_view.first_cell_offset.y - g_view.cell.height;

    int cell_x = g_view.first_cell.x + mouse_x / g_view.cell.width;
    int cell_y = g_view.first_cell.y + mouse_y / g_view.cell.height;
    int on_cell_x = mouse_x % g_view.cell.width;
    int on_cell_y = mouse_y % g_view.cell.height;

    int offset_x = 0;
    int offset_y = 0;

    switch (GetCellColor(on_cell_x, on_cell_y)) {
        case 4:
        case 5:
        case 7:
        case 8:
        case 9:
        case 10:
        case 12:
        case 13:
            offset_x = 0;
            offset_y = 0;
            break;
        case 1:
        case 3:
            offset_x = -1;
            offset_y = 0;
            break;
        case 2:
        case 6:
            offset_x = 0;
            offset_y = 1;
            break;
        case 11:
        case 15:
            offset_x = 0;
            offset_y = -1;
            break;
        case 14:
        case 16:
            offset_x = 1;
            offset_y = 0;
            break;
    }

    *x = cell_x + cell_y + offset_x;
    *y = cell_x - cell_y + offset_y;
}


void DestroyCell() {
    if (g_view.cell_buffer)
        free(g_view.cell_buffer);
}

void DrawerCell(int x, int y, int x_screen, int y_screen) {
    if (IsOnMap(x, y))
        DrawSprite(g_cell_sprite, x_screen, y_screen);
}

void DrawerSelect(int x, int y, int x_screen, int y_screen) {
    if (IsOnMap(x, y)) {
        if (InMouseSelect(x, y))
            DrawSprite(g_select_sprite, x_screen, y_screen);
        if (CanMove(x, y)) {
            DrawSprite(g_canmove_sprite, x_screen, y_screen);
        }
    }
}

void DrawerShip(int x, int y, int x_screen, int y_screen) {
    if (IsOnMap(x, y)) {
        Ship* ship = ShipOnMap(x, y);
        if (ship) {
            DrawShip(ship, x_screen, y_screen);
        }
    }
}

void TraceRow(int map_x, int map_y, int n,
        int offset_x, int offset_y, t_draw_func draw_func) {
    int i;
    for (i = 0; i < n; ++i) {
        (draw_func)(map_x + i, map_y + i, offset_x + i * g_view.cell.width, offset_y);
    }
}

void TraceDraw(t_draw_func draw_func) {
    int j;
    for (j = 0; j < g_view.view.height; ++j) {
        if (!j) {
            TraceRow(g_view.first_cell.x + g_view.first_cell.y + j - 1,
                    g_view.first_cell.x - g_view.first_cell.y - j,
                    g_view.view.width + 1,
                    g_view.first_cell_offset.x - g_view.cell.width / 2,
                    g_view.first_cell_offset.y + g_view.cell.height * j - g_view.cell.height - g_view.cell.height / 2,
                    draw_func);
        }
        TraceRow(g_view.first_cell.x + g_view.first_cell.y + j,
                g_view.first_cell.x - g_view.first_cell.y - j,
                g_view.view.width,
                g_view.first_cell_offset.x,
                g_view.first_cell_offset.y + g_view.cell.height * j - g_view.cell.height,
                draw_func);
        TraceRow(g_view.first_cell.x + g_view.first_cell.y + j,
                g_view.first_cell.x - g_view.first_cell.y - j - 1,
                g_view.view.width + 1,
                g_view.first_cell_offset.x - g_view.cell.width / 2,
                g_view.first_cell_offset.y + g_view.cell.height * j - g_view.cell.height / 2,
                draw_func);

    }
}

void DrawView() {
    TraceDraw(&DrawerCell);
    TraceDraw(&DrawerSelect);
    TraceDraw(&DrawerShip);
    DrawControl(g_view.window.width - g_control.width, 0);
}

void MoveView(int deltax, int deltay) {
    g_view.offset.x += deltax;
    g_view.offset.y += deltay;

    RecalcCellOffset();
}
